#pragma once 

#include <string>
#include <string.h>
#include <iostream>
#include <assert.h>
using namespace std;

namespace qds
{
    class string
    {
        public:
            //普通迭代器
            typedef char* iterator;

            iterator begin()
            {
                return _str;
            }

            iterator end()
            {
                return _str + _size;
            }

            //const迭代器
            typedef const char* const_iterator;

            const_iterator begin() const 
            {
                return _str;
            }

            const_iterator end() const 
            {
                return _str + _size;
            }

            //默认构造函数
            string(const char* str = "")
                :_size(strlen(str))
                ,_capacity(_size)
                {
                    _str = new char[_capacity + 1];
                    strcpy(_str, str);
                }

            //因为交换使用次数比较多,所以我们自己实现一个
            void swap(string& s)
            {
                //这里注意,自己实现的swap和c++提供的swap的命名问题,或者是指定类域的问题
                std::swap(_str, s._str);
                std::swap(_size, s._size);
                std::swap(_capacity, s._capacity);
            }
            
            //拷贝构造函数
            //传统写法
            //string(const string& s)
            //    :_size(s._size)
            //    ,_capacity(s._capacity)
            //    {
            //        _str = new char[_capacity + 1];
            //        strcpy(_str, s._str);
            //    }

            //现代写法
            string(const string& s)
                :_str(nullptr)
                , _size(0)
                , _capacity(0)
            {
                string tmp(s._str);
                swap(tmp);
            }

            //操作符=的重载
            //传统写法
            //string&  operator=(const string& s)
            //{
            //    //错误写法一:没有给自己自己赋值进行判断,直接释放了原空间,导致strlen()找到的'\0'位置不对
            //    delete[] _str;
            //    _str = new char[s._capacity + 1];
            //    strcpy(_str, s._str);
            //
            //    //错误写法二:如果new失败了,但是你把之前的空间释放了,也会导致错误
            //    if (this != &s)
            //    {
            //      delete[] _str;
            //      _str = new char[s._capacity + 1]
            //      strcpy(_str, s._str);
            //    }
            //    
            //    //这里是用一个tmp来进行接收,new失败了,会进行抛异常,不会导致出问题
            //    if (this != &s)
            //    {
            //        char* tmp = new char[s._capacity + 1];
            //        strcpy(tmp, s._str);
            //        delete[] _str;
            //        _str = tmp;
            //        _size = s._size;
            //        _capacity = s._capacity;
            //    }
            //    return *this;
            //}
            
            //string& operator=(const char* str)
            //{
            //    char* tmp = new char[strlen(str) + 1];
            //    strcpy(tmp, str);
            //    delete[] _str;
            //    _str = tmp;
            //    _size = strlen(str);
            //    _capacity = _size;
            //    return *this;
            //}

            //string& operator=(const char ch)
            //{
            //    char* tmp = new char[2];
            //    tmp[0] = ch;
            //    tmp[1] = '\0';
            //    delete[] _str;
            //    _str = tmp;
            //    _size = 1;
            //    _capacity = _size;
            //    return *this;
            //}

            //现代写法
            string& operator=(string s)
            {
                swap(s);
                return *this;
            }

            string& operator=(const char* str)
            {
                string s(str);
                swap(s);
                return *this;
            }

            string& operator=(const char& ch)
            {
                char* tmp = new char[2];
                tmp[0] = ch;
                tmp[1] = '\0';
                string s(tmp);
                swap(s);
                return *this;
            }

            //操作符[]的重载
            char& operator[](size_t pos)
            {
                assert(pos < _size);
                return _str[pos];
            }

           const char& operator[](size_t pos) const
           {
               assert(pos < _size);
               return _str[pos];
           }

            //析构函数
            ~string()
            {
                delete[] _str;
                _str = nullptr;
                _size = _capacity = 0;
            }

            //返回string对应的c-string 
            const char* c_str() const
            {
                return _str;
            }
            //返回string对应的长度
            size_t size() const
            {
                return _size;
            }

            //预留至少n个容量
            void reserve(size_t n)
            {
                //n > _capacity才进行操作
                //n <= _capacity都不进行操作
                if (n > _capacity)
                {
                    char* tmp = new char[n + 1];
                    strcpy(tmp, _str);
                    delete[] _str;
                    _str = tmp;
                    _capacity = n;
                }

            }

            //重新调整长度
            void resize(size_t n, char ch = '\0')
            {
                if (n <= _size)
                {
                    _size = n;
                    _str[_size] = '\0';
                }
                else 
                {
                    if (n > _capacity)
                    {
                        reserve(n);
                    }
                    memset(_str + _size, ch, n - _size);
                    _size = n;
                    _str[_size] = '\0';
                }
            
            }

            //尾插一个字符到这个串
            void push_back(char ch)
            {
                //注意这里,需要考虑_capacity为0的情况,单独处理,不然会越界
                //if (_size == _capacity)
                //{
                //    reserve(_capacity == 0 ? 4 : _capacity * 2);
                //}

                //_str[_size] = ch;
                //++_size;
                //_str[_size] = '\0';
                
                //可以对insert进行复用
                insert(_size, ch);
            }

            //尾插一个字符串
            string& append(const char* str)
            {
                //size_t len = strlen(str);
                //if (_size + len > _capacity)
                //{
                //    reserve(_size + len);
                //}
                //
                //strcpy(_str + _size, str);
                //_size += len;
                //return *this;
                
                //同样对insert进行复用
               return insert(_size, str);
            }

            //尾插一个string对象
            string& append(const string& s)
            {
                append(s.c_str());
                return *this;
            }

            //+=运算符重载
            string& operator+=(const char* str)
            {
                return append(str);
            }

            string& operator+=(const string& s)
            {
                return append(s);
            }

            string& operator+=(char c)
            {
                push_back(c);
                return *this;
            }

            //查找串中是否有对应的字符
            size_t find(char ch) const
            {
                for (size_t i = 0; i < _size; ++i)
                {
                    if (_str[i] == ch)
                    {
                        return i;
                    }
                }
                return npos;
            }

           //查找串中是否有对应的子串
           size_t find(const char* str, size_t pos = 0) const
           {
               //前面的参数是开始寻找的地址,后面的是需要寻找的字串
               const char* ptr = strstr(_str + pos, str);

               if (ptr == nullptr)
               {
                   return npos;
               }
               else
               {
                   return ptr - _str;
               }
           }

            //根据合适的位置进行插入数据
            string& insert(size_t pos, char ch)
            {
                assert(pos <= _size);
                if (_size == _capacity)
                {
                    reserve(_capacity == 0 ? 4 : _capacity * 2);
                }

                //插入数据往后的位置数据全部往后挪动
                //错误写法
                //size_t end = _size;
                //while (end >= pos)
                //{
                //    //如果这里插入的位置是0,那么--end就会出错
                //    _str[end + 1] = _str[end];
                //    --end;
                //}
                
                //正确写法
                //这种写法不会存在--end会出错
                size_t  end = _size + 1;
                while (end > pos)
                {
                    _str[end] = _str[end - 1];
                    --end;
                }

                _str[pos] = ch;
                ++_size;
                return *this;
            }

            string& insert(size_t pos, const char* str)
            {
                assert(pos <= _size);
                size_t len = strlen(str);
                if (_size + len > _capacity)
                {
                    reserve(_size + len);
                }

                //这里end不存在越界的情况,所以不需要+1
                //错误写法:
                //虽然end - len可能会越界,但是不影响,之后的strncpy会进行矫正
                //这种越界的抽查,有些时候编译器不会检查出来
                //虽然最后的结果是正确的,但是中间的过程是错误的
                //size_t end = _size + len;
                //while (end > pos)
                //{
                //    _str[end] = _str[end - len];
                //    --end;
                //}
                
                //正确写法:
                size_t end = _size + len;
                //所以注意控制好循环条件
                while (end >= pos + len)
                {
                    _str[end] = _str[end - len];
                    --end;
                }

                strncpy(_str + pos, str, len);
                _size += len;
                return *this;
            }

            //删除指定位置后的数据
            string& erase(size_t pos = 0, size_t len = npos)
            {
                assert(pos < _size);

                if (len == npos || len + pos >= _size)
                {
                    _str[pos] = '\0';
                    _size = pos;
                }
                else
                {
                    strcpy(_str + pos, _str + pos + len);
                    _size -= len;
                }
                return *this;
            }

            void clear()
            {
                _str[0] = '\0';
                _size = 0;
            }


private:
        char* _str;
        //只包含了有效字符,没有包含'\0'
        size_t _size;
        size_t _capacity;

        static const size_t npos;
    };

    const size_t string::npos = -1;

    //实现大小运算符的重载,一般是在类里面进行实现,因为会涉及到成员变量的访问
    //这里实现成全局函数,因为[]可以进行成员变量的访问
    //当然自己也可以写get函数,获取到成员变量引用的返回,从而实现全局函数
    //注意:实现大小运算符的重载,优先实现==和<或者是==和>,其他的运算符都可以由这两个运算符组合出来
    bool operator<(const string& s1, const string& s2) 
    {
        //写法一:自己实现
        //size_t i1 = 0, i2 = 0;
        //while (i1 < s1.size() && i2 < s2.size())
        //{
        //    if (s1[i1] < s2[i1])
        //    {
        //        return true;
        //    }
        //    else if (s1[i1] > s2[i2])
        //    {
        //        return false;
        //    }
        //    {
        //        ++i1;
        //        ++i2;
        //    }
        //}
        ////根据上面while循环的条件,可知,要么是s1走完了,要么是s2走完了,或者是两者一起走完了
        ////如果是s1走完了,那么就意味着,i2 < s2.size(),所以s2比s1长,所以s1 < s2
        ////如果是s2走完了,那么就意味着,i2 = s2.size(),所以s2比s1短,所以s1 > s2
        ////如果s1和s2同时走完,意味着,i2 = s2.size(),s1和s2同样长.
        //return i2 < s2.size() ? true : false;
        
        //写法二:运用库函数
        //如果s1小于s2那么strcmp()返回值是一个小于零的数,那么条件为真,返回true
        //如果s1大于等于s2那么strcmp()返回值是一个大于零和等于零的数,那么条件为假,返回false
        return strcmp(s1.c_str(), s2.c_str()) < 0;
    }

    bool operator==(const string& s1, const string& s2)
    {
        //写法一:自己实现
        //if (s1.size() != s2.size())
        //{
        //    return false;
        //}

        //for (size_t i = 0; i < s1.size(); ++i)
        //{
        //    if (s1[i] != s2[i])
        //    {
        //        return false;
        //    }
        //}
        //return true;
        
        //写法二:调用库函数
        return strcmp(s1.c_str(), s2.c_str()) == 0;
    }

    bool operator<=(const string& s1, const string& s2)
    {
        return s1 < s2 || s1 == s2;
    }

    bool operator>(const string& s1, const string& s2)
    {
        return !(s1 <= s2);
    }

    bool operator>=(const string& s1, const string& s2)
    {
        return !(s1 < s2);
    }

    bool operator!=(const string& s1, const string& s2)
    {
        return !(s1 == s2);
    }

    //一般operator<<和operator>>的重载都是实现全局函数,如果是成员函数,因为隐含的this指针,倒是函数调用的时候不符合我们的使用习惯
    //如果没有获取成员变量的成员函数,那么实现为全局函数时,需要使用友元函数
    ostream& operator<<(ostream& _cout, const string& s)
    {
        //自己写
        for (size_t i = 0; i < s.size(); ++i)
        {
            cout << s[i];
        }

        //范围for
        //for (auto ch : s)
        //{
        //    cout << ch;
        //}

        //错误写法
        //cout << s.c_str();
        //如果出现这种情况,那么就会出错,其他大部分场景两者是差不多的
        //string s1("abcde"); s1 += '\0'; s1 += "efg";
        //如果是这种错误写法,那么最后输出的结果就是abcde
        //如果是正确的写法,那么最后的输出结果就是abcde efg

        return _cout;
    }

    istream& operator>>(istream& _cin, string& s)
    {
        s.clear();

        char ch = _cin.get();
        while(ch != '\n')
        {
            s += ch;
            ch = _cin.get();
        }
        return _cin;
    }
}
